/* Copyright (c) 2025 Beijing Semidrive Technology Corporation
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/** *****************************************************************************************************
 * \file     Eep.c                                                                            *
 * \brief    AUTOSAR 4.3.1 MCAL can Driver                                                             *
 *                                                                                                      *
 * <table>                                                                                              *
 * <tr><th>Date           <th>Version                                                                   *
 * <tr><td>2024/11/04     <td>1.0.0                               *
 * </table>                                                                                             *
 *******************************************************************************************************/

#ifdef __cplusplus
extern "C" {
#endif /* #ifdef __cplusplus */

#include "Dem.h"
#include "SchM_Eep.h"
#include "regs_base.h"
#include "Mcal_MemLibc.h"
#include "Eep.h"
#include "Semag.h"
#include "e3_device.h"
#include "Eep_FaultInject.h"

/* PRQA S 0306,0314,0326,0488,1505,1514,2755,2877,3673,3678 EOF */

#if (EEP_NON_CACHEABLE_NEEDED == STD_OFF)
#include "Mcal_Cache.h"
#define EEP_CLEAN_CACHE(addr, size)         (void) Mcal_CleanCache(addr, size)
#define EEP_INVALIDATE_CACHE(addr, size)    (void) Mcal_InvalidateCache(addr, size)
#define EEP_FLUSH_CACHE(addr, size)         (void) Mcal_FlushCache(addr, size)
#else
#define EEP_CLEAN_CACHE(...)
#define EEP_INVALIDATE_CACHE(...)
#define EEP_FLUSH_CACHE(...)
#endif /* #if (EEP_NON_CACHEABLE_NEEDED == STD_OFF) */

#ifdef EEP_DEBUG_LOG
#include "debug.h"
#define EEP_DEBUG(fmt, args...) PRINT("%s %d: " fmt, __FUNCTION__, __LINE__, ##args)
#else
#define EEP_DEBUG(...)
#endif /* EEP_DEBUG */

#if (EEP_DEV_ERROR_DETECT == STD_OFF)
#define EEP_DET_REPORT_ERROR(...)
#define EEP_DET_REPORT_TRANS_FAULT(...)
#define EEP_DET_REPORT_RUNTIME_ERROR(...)
#else
#include "Det.h"
#define EEP_DET_REPORT_ERROR(ModuleId, InstanceId, ApiId, ErrorId) (void)Det_ReportError(ModuleId, InstanceId, ApiId, ErrorId)
#define EEP_DET_REPORT_TRANS_FAULT(ModuleId, InstanceId, ApiId, ErrorId) (void)Det_ReportTransientFault(ModuleId, InstanceId, ApiId, ErrorId)
#define EEP_DET_REPORT_RUNTIME_ERROR(ModuleId, InstanceId, ApiId, ErrorId) (void)Det_ReportRuntimeError(ModuleId, InstanceId, ApiId, ErrorId)
#endif /*EEP_DEV_ERROR_DETECT == STD_OFF*/


/********************************************************************************************************
 *                                 Imported Compiler Switch Checks                                      *
 *******************************************************************************************************/
#ifndef EEP_SW_MAJOR_VERSION
#error "EEP_SW_MAJOR_VERSION is not defined."
#endif

#ifndef EEP_SW_MINOR_VERSION
#error "EEP_SW_MINOR_VERSION is not defined."
#endif

#ifndef EEP_SW_PATCH_VERSION
#error "EEP_SW_PATCH_VERSION is not defined."
#endif

/* AUTOSAR Secification File Version Check */
#ifndef EEP_AR_RELEASE_MAJOR_VERSION
#error "EEP_AR_RELEASE_MAJOR_VERSION is not defined."
#endif

/* AUTOSAR Secification File Version Check */
#ifndef EEP_AR_RELEASE_MINOR_VERSION
#error "EEP_AR_RELEASE_MINOR_VERSION is not defined."
#endif

/* AUTOSAR Secification File Version Check */
#ifndef EEP_AR_RELEASE_REVISION_VERSION
#error "EEP_AR_RELEASE_REVISION_VERSION is not defined."
#endif

/** \brief  AUTOSAR Major and Minor release version check. */
#if(EEP_AR_RELEASE_MAJOR_VERSION != 4U)
#error "EEP_AR_RELEASE_MAJOR_VERSION does not match."
#endif

#if(EEP_AR_RELEASE_MINOR_VERSION != 3U)
#error "EEP_AR_RELEASE_MINOR_VERSION does not match."
#endif

#if(EEP_AR_RELEASE_REVISION_VERSION != 1U)
#error "EEP_AR_RELEASE_REVISION_VERSION does not match."
#endif

/********************************************************************************************************
 *                                 Private Macro definition                                             *
 *******************************************************************************************************/
#define EEP_HOST_MASK ((1u << EEP_CFG_HOST_MAX_NUM) - 1u)
#define EEP_OFFSET_OF(type, member) (uint32)&(((type*)0)->member)
#define EEP_CONTAINER_OF(ptr, type, member) \
    ((type *)((uint32)(ptr) - EEP_OFFSET_OF(type, member)))

#define EEP_SW_VENDOR (0x8Cu)

/********************************************************************************************************
 *                                    Private Type Declarations                                         *
 *******************************************************************************************************/
typedef enum
{
    EEP_JOB_PREPARE             = 0x00u,
#if 0 /* Not use, code reserve */
    EEP_JOB_EXECUTE             = 0x01u,
    EEP_JOB_VERIFY              = 0x02u,
#endif
    EEP_JOB_POST                = 0x03u,
    EEP_JOB_UNKNOW              = 0xFFu,
} Eep_JobStatusType;

typedef struct Eep_ListNode
{
    struct Eep_ListNode *prev;
    struct Eep_ListNode *next;
} Eep_ListNodeType;

typedef struct
{
    MemIf_StatusType            status;
    Eep_InterfaceServiceIdType serviceId;
    Eep_AddressType             startAddr;
    Eep_LengthType              length;
    Eep_LengthType              hasDoneBytes;
    Eep_LengthType              hasPrepareDoneBytes;
    uint8                       *destPtr;
    const uint8                 *sourcePtr;
    Eep_JobStatusType           jobStatus;
    MemIf_JobResultType         jobResult;
#if (EEP_CALL_CYCLES != 0u)
    boolean                     timeoutCheckEnable;
    uint32                      timeoutCycleCount;
#endif /* #if (EEP_CALL_CYCLES != 0u) */
} Eep_JobInfoType;

typedef struct
{
    /* Run time parameter */
    Eep_ListNodeType            node;
    Eep_JobInfoType             jobInfo;

    /* Static destination configuration, determined after initialization */
    Eep_HostContextType         *hostCtxTable;
    uint8                       hostNum;
    Eep_AddressType             mergedBaseAddress;
    Eep_AddressType             mergedRdAxiBaseAddress;
    Eep_AddressType             mergedWrAxiBaseAddress;
    const Eep_BlockConfigType   *blockConfig;
} Eep_BlockInfoType;

typedef struct {
    const Eep_ConfigType *configPtr;
    Eep_HostContextType hostCtx[EEP_CFG_HOST_MAX_NUM];
    Eep_LengthType totalSize;
    Eep_BlockInfoType blockInfo[EEP_CFG_INIT_BLOCK_NUM];
    Eep_ListNodeType pendingJobList[EEP_CFG_BLOCK_PRIORITY_MAX];
    MemIf_JobResultType  lastJobResult;
    MemIf_ModeType              memIfMode;
    Eep_LengthType              maxNumBytesWrite;
    Eep_LengthType              maxNumBytesRead;
} Eep_ContextType;

/********************************************************************************************************
 *                                  Private Variable Definitions                                        *
 *******************************************************************************************************/
#define EEP_START_SEC_VAR_NO_INIT_UNSPECIFIED
#include "Eep_MemMap.h"

static Eep_ContextType Eep_Context[CPU_MAX_CORE_NUMBER];

#if (EEP_CFG_IMAGE_AREA_NUM)
EepImageInfoType Eep_ImageInfo[EEP_CFG_IMAGE_AREA_NUM];
#endif /* EEP_CFG_IMAGE_AREA_NUM */

#define EEP_STOP_SEC_VAR_NO_INIT_UNSPECIFIED
#include "Eep_MemMap.h"

#define EEP_START_SEC_CONST_UNSPECIFIED
#include "Eep_MemMap.h"


#define EEP_STOP_SEC_CONST_UNSPECIFIED
#include "Eep_MemMap.h"

/********************************************************************************************************
 *                                  Private Function Declarations                                       *
 *******************************************************************************************************/

#define EEP_START_SEC_CODE
#include "Eep_MemMap.h"

/** *****************************************************************************************************
 * \brief This function Initialize the chain table header
 *
 * \verbatim
 * Syntax             : void Eep_ListInitialize(Eep_ListNodeType *list)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Non reentrant
 *
 * Parameters (in)    : list - Point of the chain table header
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : None
 *
 * Description        : Initialize the chain table header
 * \endverbatim
 *******************************************************************************************************/
static void Eep_ListInitialize(Eep_ListNodeType *list)
{
    list->prev = list;
    list->next = list;
}

/** *****************************************************************************************************
 * \brief This function Remove a node from a linked list
 *
 * \verbatim
 * Syntax             : void Eep_ListDelete(Eep_ListNodeType *item)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Non reentrant
 *
 * Parameters (in)    : item - Point of the node
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : None
 *
 * Description        : Remove a node from a linked list
 * \endverbatim
 *******************************************************************************************************/
static void Eep_ListDelete(Eep_ListNodeType *item)
{
    item->next->prev = item->prev;
    item->prev->next = item->next;
    item->prev = NULL_PTR;
    item->next = NULL_PTR;
}

/** *****************************************************************************************************
 * \brief This function Delete a node from the head of the chain table
 *
 * \verbatim
 * Syntax             : Eep_ListNodeType *Eep_ListRemoveHead(Eep_ListNodeType *list)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Non reentrant
 *
 * Parameters (in)    : list - Point of the chain table header
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : Point of the node
 *
 * Description        : Delete a node from the head of the chain table
 * \endverbatim
 *******************************************************************************************************/
static Eep_ListNodeType *Eep_ListRemoveHead(Eep_ListNodeType *list)
{
    Eep_ListNodeType *item = NULL_PTR;

    if (list->next != list)
    {
        item = list->next;
        Eep_ListDelete(item);
    }

    return item;
}

/** *****************************************************************************************************
 * \brief This function Add a node to the end of the chain table
 *
 * \verbatim
 * Syntax             : void Eep_ListAddTail(
 *                          Eep_ListNodeType *list, Eep_ListNodeType *item)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Non reentrant
 *
 * Parameters (in)    : list - Point of the chain table header
 *                      item - Point of the node
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : None
 *
 * Description        : Add a node to the end of the chain table
 * \endverbatim
 *******************************************************************************************************/
static void Eep_ListAddTail(Eep_ListNodeType *list, Eep_ListNodeType *item)
{
    item->prev = list->prev;
    /* PRQA S 2916 1 */
    item->next = list;
    list->prev->next = item;
    list->prev = item;
}

/** *****************************************************************************************************
 * \brief This function return the result if a node is in a linked list
 *
 * \verbatim
 * Syntax             : boolean Eep_ListInList(Eep_ListNodeType *item)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Non reentrant
 *
 * Parameters (in)    : item - Point of the node
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : FALSE - node is not in a linked list
 *                      TRUE - node is in a linked list
 *
 * Description        : Determine if a node is in a linked list
 * \endverbatim
 *******************************************************************************************************/
static boolean Eep_ListInList(Eep_ListNodeType *item)
{
    boolean ret = TRUE;

    if ((NULL_PTR == item->prev) && (NULL_PTR == item->next))
    {
        ret = FALSE;
    }

    return ret;
}

/** *****************************************************************************************************
 * \brief This function Restore the context of operating
 *
 * \verbatim
 * Syntax             : void Eep_ResetOrders(Eep_JobInfoType *pJob)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Non reentrant
 *
 * Parameters (in)    : pJob - Point of job's infomation (Eep_JobInfoType)
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : None
 *
 * Description        : Restore the context of operating
 * \endverbatim
 * Traceability       :
 *******************************************************************************************************/
static void Eep_ResetOrders(Eep_BlockInfoType *pBlock)
{
    Eep_ContextType *eepContextPtr = &Eep_Context[Mcal_GetCpuIndex()];
    SchM_Enter_Eep_EEP_EXCLUSIVE_AREA_00();
    pBlock->jobInfo.serviceId = EEP_SERVICE_ID_MAX;
    pBlock->jobInfo.jobStatus = EEP_JOB_UNKNOW;

    if (NULL_PTR != eepContextPtr->configPtr)
    {
        eepContextPtr->lastJobResult = pBlock->jobInfo.jobResult;
        pBlock->jobInfo.status = MEMIF_IDLE;
    }
    else
    {
        pBlock->jobInfo.status = MEMIF_UNINIT;
    }

    if (TRUE == Eep_ListInList(&pBlock->node))
    {
        Eep_ListDelete(&pBlock->node);
    }

    SchM_Exit_Eep_EEP_EXCLUSIVE_AREA_00();
}

/** *****************************************************************************************************
 * \brief This function Return the block info where the target address is located
 *
 * \verbatim
 * Syntax             : uint8 Eep_GetBlockInfo(
 *                          Eep_AddressType beginAddress, Eep_LengthType length,
 *                          Eep_BlockInfoType **blockInfo)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Non reentrant
 *
 * Parameters (in)    : beginAddress - Address in mram memory
 *                      length - The length of the operation
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : blockInfo - Return the block info where the target address is located
 *
 * Return value       : 0 - Success
 *                      !0 - Failed
 *
 * Description        : Return the block info where the target address is located
 * \endverbatim
 * Traceability       : None
 *******************************************************************************************************/
static uint8 Eep_GetBlockInfo(Eep_AddressType beginAddress, Eep_LengthType length,
        Eep_BlockInfoType **blockInfo)
{
    uint8 ret;
    uint8 i;
    Eep_ContextType *eepContextPtr = &Eep_Context[Mcal_GetCpuIndex()];

    for (i = 0; i < EEP_CFG_INIT_BLOCK_NUM; i++)
    {
        ret = EEP_E_PARAM_ADDRESS;
        if ((beginAddress >= eepContextPtr->blockInfo[i].blockConfig->baseAddress) &&
            (beginAddress < (eepContextPtr->blockInfo[i].blockConfig->baseAddress +
                 eepContextPtr->blockInfo[i].blockConfig->size)))
        {
            ret = EEP_E_PARAM_LENGTH;
            if ((beginAddress + length) <=
                (eepContextPtr->blockInfo[i].blockConfig->baseAddress +
                 eepContextPtr->blockInfo[i].blockConfig->size))
            {
                ret = E_OK;
                *blockInfo = &eepContextPtr->blockInfo[i];
            }
            break;
        }
    }

    return ret;
}

/** *****************************************************************************************************
 * \brief This function is Tail treatment of one operation.
 *
 * \verbatim
 * Syntax             : void Eep_JobEnd( Eep_BlockInfoType *pBlock
 *                          Eep_ExtendedProductionErrorType stRet)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Non reentrant
 *
 * Parameters (in)    : pJob - Point of job's infomation (Eep_BlockInfoType)
 *                      stRet - The return value of the current operation
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : None
 *
 * Description        : Report the result of the operation and callback
 * \endverbatim
 * Traceability       :
 *******************************************************************************************************/
static void Eep_JobEnd(Eep_BlockInfoType *pBlock, Eep_ExtendedProductionErrorType stRet)
{
    EEP_DEBUG("End 0x%x job (%d %d)\n", &pBlock->node, pBlock->jobInfo.serviceId, stRet);

    if (stRet == EEP_E_NO_ERROR)
    {
        pBlock->jobInfo.jobResult = MEMIF_JOB_OK;

        Eep_ResetOrders(pBlock);

        /* Callback */
        if (NULL_PTR != pBlock->blockConfig->jobEndNotification)
        {
            pBlock->blockConfig->jobEndNotification();
        }
    }
    else
    {
        if (EEP_E_COMPARE_FAILED == stRet)
        {
            pBlock->jobInfo.jobResult = MEMIF_BLOCK_INCONSISTENT;
        }
        else if (EEP_E_OPERATION_CANCEL == stRet)
        {
            pBlock->jobInfo.jobResult = MEMIF_JOB_CANCELED;
        }
        else
        {
            pBlock->jobInfo.jobResult = MEMIF_JOB_FAILED;
        }

        Eep_ResetOrders(pBlock);

        /* Callback */
        if (NULL_PTR != pBlock->blockConfig->jobErrorNotification)
        {
            pBlock->blockConfig->jobErrorNotification();
        }
    }
}

/** *****************************************************************************************************
 * \brief This function is the Head treatment of one operation
 *
 * \verbatim
 * Syntax             : Eep_BlockInfoType* Eep_JobPrepare(
 *                          Eep_AddressType targetAddress, Eep_LengthType length,
 *                          Eep_InterfaceServiceIdType apiId)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Non reentrant
 *
 * Parameters (in)    : targetAddress - Address in mram memory
 *                      length - The length of the operation
 *                      apiId - Operation index
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : Successfully: The point of Jobs Infomation (Eep_JobInfoType);
 *                      Failed: NULL_PTR
 *
 * Description        : Checking parameters and preparing resources
 * \endverbatim
 * Traceability       :
 *******************************************************************************************************/
static Eep_BlockInfoType *Eep_JobPrepare(Eep_AddressType targetAddress, Eep_LengthType length,
                            Eep_InterfaceServiceIdType apiId)
{
    Eep_BlockInfoType *pBlock = NULL_PTR;
    uint8 ret;
    Eep_ContextType *eepContextPtr = &Eep_Context[Mcal_GetCpuIndex()];

    if (0u == length)
    {
        ret = EEP_E_PARAM_LENGTH;
    }
    else if (NULL_PTR == eepContextPtr->configPtr)
    {
        ret = EEP_E_UNINIT;
    }
    else
    {
        ret = Eep_GetBlockInfo(targetAddress, length, &pBlock);
        SchM_Enter_Eep_EEP_EXCLUSIVE_AREA_00();
        if (NULL_PTR == pBlock)
        {
            /* Do nothing */
        }
        else if (MEMIF_BUSY == pBlock->jobInfo.status)
        {
            ret = EEP_E_BUSY;
        }
        else
        {
            pBlock->jobInfo.status = MEMIF_BUSY;
            pBlock->jobInfo.serviceId = apiId;
            pBlock->jobInfo.startAddr = targetAddress;
            pBlock->jobInfo.length = length;
            pBlock->jobInfo.hasDoneBytes = 0;
            pBlock->jobInfo.hasPrepareDoneBytes = 0;
            pBlock->jobInfo.jobStatus = EEP_JOB_PREPARE;
            pBlock->jobInfo.jobResult = MEMIF_JOB_PENDING;
#if (EEP_CALL_CYCLES != 0u)
            pBlock->jobInfo.timeoutCheckEnable = FALSE;
#endif /* #if (EEP_CALL_CYCLES != 0u) */
        }
        SchM_Exit_Eep_EEP_EXCLUSIVE_AREA_00();
    }

    if (E_OK != ret)
    {
        EEP_DET_REPORT_ERROR(EEP_MODULE_ID, 0, (uint8)apiId, ret);
        pBlock = NULL_PTR;
    }

    return pBlock;
}

/** *****************************************************************************************************
 * \brief This function Inserting a Job into a Chained List
 *
 * \verbatim
 * Syntax             : void Eep_JobInsert(
 *                                  Eep_BlockInfoType* pBlock)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Non reentrant
 *
 * Parameters (in)    : pBlock - Point of block's information
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : None
 *
 * Description        : Inserting a Job into a Chained List
 * \endverbatim
 * Traceability       :
 *******************************************************************************************************/
static void Eep_JobInsert(Eep_BlockInfoType *pBlock)
{
    Eep_ContextType *eepContextPtr = &Eep_Context[Mcal_GetCpuIndex()];
    EEP_DEBUG("Insert 0x%x job (%d %d)\n", &pBlock->node, pBlock->jobInfo.serviceId,
        pBlock->jobInfo.jobStatus);
    SchM_Enter_Eep_EEP_EXCLUSIVE_AREA_00();
    Eep_ListAddTail(&eepContextPtr->pendingJobList[pBlock->blockConfig->priority], &pBlock->node);
    SchM_Exit_Eep_EEP_EXCLUSIVE_AREA_00();
}


/** *****************************************************************************************************
 * \brief This function Rerturn the result of Determine if there is unfinished business
 *
 * \verbatim
 * Syntax             : boolean Eep_IsJobsPending( void )
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Non reentrant
 *
 * Parameters (in)    : None
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : TRUE - There is unfinished jobs
 *                      FALSE - No unfinished jobs
 *
 * Description        : Determine if there is unfinished business
 * \endverbatim
 * Traceability       :
 *******************************************************************************************************/
static boolean Eep_IsJobsPending(void)
{
    boolean ret = FALSE;
    uint8 i;
    Eep_ContextType *eepContextPtr = &Eep_Context[Mcal_GetCpuIndex()];

    SchM_Enter_Eep_EEP_EXCLUSIVE_AREA_00();
    if (NULL_PTR != eepContextPtr->configPtr)
    {
        for (i = 0; i < EEP_CFG_INIT_BLOCK_NUM; i++)
        {
            if (MEMIF_BUSY == eepContextPtr->blockInfo[i].jobInfo.status)
            {
                ret = TRUE;
                break;
            }
        }
    }
    SchM_Exit_Eep_EEP_EXCLUSIVE_AREA_00();

    return ret;
}

/** *****************************************************************************************************
 * \brief This function Rerturn the point of unfinished job
 *
 * \verbatim
 * Syntax             : Eep_BlockInfoType* Eep_GetPendingJob()
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Non reentrant
 *
 * Parameters (in)    : pCtx - Point of block context
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : Point of unfinished job
 *
 * Description        : Rerturn the point of unfinished job
 * \endverbatim
 * Traceability       :
 *******************************************************************************************************/
static Eep_BlockInfoType *Eep_GetPendingJob(void)
{
    const Eep_ListNodeType *node;
    Eep_BlockInfoType *pBlock = NULL_PTR;
    uint8 i;
    Eep_ContextType *eepContextPtr = &Eep_Context[Mcal_GetCpuIndex()];
    SchM_Enter_Eep_EEP_EXCLUSIVE_AREA_00();

    for (i = EEP_CFG_BLOCK_PRIORITY_MAX; i > 0U; i--)
    {
        node = Eep_ListRemoveHead(&eepContextPtr->pendingJobList[i - 1U]);

        if (NULL_PTR != node)
        {
            /* PRQA S 2810,3432 1 */
            pBlock = EEP_CONTAINER_OF((node), Eep_BlockInfoType, node);
            break;
        }
    }

    SchM_Exit_Eep_EEP_EXCLUSIVE_AREA_00();
    return pBlock;
}

static Eep_LengthType Eep_GetHostContext(Eep_BlockInfoType *pBlock, Eep_AddressType addr,
    Eep_LengthType length, Eep_HostContextType **hostCtxPtrRet)
{
    uint8 i;
    Eep_LengthType overlapLength = 0u;
    Eep_HostContextType *hostCtxPtr;

    for (i = 0; i < pBlock->hostNum; i++)
    {
        hostCtxPtr = &pBlock->hostCtxTable[i];
        if ((addr >= hostCtxPtr->baseAddress) &&
            (addr < (hostCtxPtr->baseAddress + hostCtxPtr->size)))
        {
            overlapLength = MRAM_MIN(length, hostCtxPtr->baseAddress + hostCtxPtr->size - addr);
            if (NULL_PTR != hostCtxPtrRet)
            {
                *hostCtxPtrRet = hostCtxPtr;
            }
            break;
        }
    }
    return overlapLength;
}

static Eep_LengthType Eep_GetHostContextByArea(Eep_AddressType address, Eep_LengthType size,
    Eep_HostContextType **hostCtxPtrRet)
{
    Eep_BlockInfoType blockTemp;
    Eep_ContextType *eepContextPtr = &Eep_Context[Mcal_GetCpuIndex()];
    blockTemp.hostCtxTable = eepContextPtr->hostCtx;
    blockTemp.hostNum = EEP_CFG_HOST_MAX_NUM;
    return Eep_GetHostContext(&blockTemp, address, size, hostCtxPtrRet);
}

/** *****************************************************************************************************
 * \brief This function is The actual operation of compare data between mram and user buffer.
 *
 * \verbatim
 * Syntax             : Eep_ExtendedProductionErrorType Eep_CompareProcess(
 *                              Eep_BlockInfoType *pBlock)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Non reentrant
 *
 * Parameters (in)    : pJob - Point of job's infomation
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : EEP_E_NO_ERROR: Operation successful
 *                      Others: Operation failed
 *
 * Description        : compare data between mram and user buffer
 * \endverbatim
 * Traceability       :
 *******************************************************************************************************/
static Eep_ExtendedProductionErrorType  Eep_CompareProcess(Eep_BlockInfoType *pBlock)
{
    Eep_HostContextType *hostCtxPtr = NULL_PTR;
    Eep_LengthType readSize;
    Eep_ExtendedProductionErrorType stRet = EEP_E_NO_ERROR;
    Eep_AddressType currentStartAddr = pBlock->jobInfo.startAddr + pBlock->jobInfo.hasDoneBytes;
    const uint8 *sourceBuffer = &pBlock->jobInfo.sourcePtr[pBlock->jobInfo.hasDoneBytes];
    Eep_LengthType residualSize = pBlock->jobInfo.length - pBlock->jobInfo.hasDoneBytes;
    Eep_ContextType *eepContextPtr = &Eep_Context[Mcal_GetCpuIndex()];

    if (eepContextPtr->maxNumBytesRead > 0u)
    {
        residualSize = MRAM_MIN(eepContextPtr->maxNumBytesRead, residualSize);
    }

    if (pBlock->mergedRdAxiBaseAddress != 0u)
    {
        EEP_INVALIDATE_CACHE(pBlock->mergedRdAxiBaseAddress + currentStartAddr -
            pBlock->mergedBaseAddress, residualSize);

        /* Direct memcopy as a whole */
        if (E_OK != Mcal_MemCmp(
            (void*)(pBlock->mergedRdAxiBaseAddress + currentStartAddr - pBlock->mergedBaseAddress),
            (const void*)sourceBuffer, residualSize))
        {
            stRet = EEP_E_COMPARE_FAILED;
            EEP_DEBUG("Compare failed[0x%x 0x%x]\r\n", currentStartAddr, residualSize);
        }
        else
        {
            pBlock->jobInfo.hasDoneBytes += residualSize;
        }
    }
    else
    {
        /* Split the copy, if not on the same MRAM */
        readSize = Eep_GetHostContext(pBlock, currentStartAddr, residualSize, &hostCtxPtr);
        if (readSize == 0u)
        {
            stRet = EEP_E_READ_FAILED;
            EEP_DEBUG("Can not get host context (0x%x 0x%x)\r\n", currentStartAddr, residualSize);
        }
        else
        {
            (void) Mram_HostRdBufferInvalidate(&hostCtxPtr->mramHost);

            EEP_INVALIDATE_CACHE(hostCtxPtr->mramHost.config.axiRdBase +
                        currentStartAddr - hostCtxPtr->baseAddress, readSize);

            if (E_OK != Mcal_MemCmp(
                (void*)(hostCtxPtr->mramHost.config.axiRdBase + currentStartAddr - hostCtxPtr->baseAddress),
                (const void*)sourceBuffer, readSize))
            {
                stRet = EEP_E_COMPARE_FAILED;
                EEP_DEBUG("Compare failed[0x%x 0x%x]\r\n", currentStartAddr, readSize);
            }
            else
            {
                pBlock->jobInfo.hasDoneBytes += readSize;
            }
        }
    }

    if (stRet != EEP_E_NO_ERROR)
    {
        EEP_DET_REPORT_TRANS_FAULT(EEP_MODULE_ID, 0, (uint8)EEP_COMPARE_SERVICE_ID, stRet);
    }

    return stRet;
}

static void Eep_EraseTry(Eep_HostContextType *hostCtxPtr, Eep_AddressType addr,
    Eep_LengthType size, Eep_LengthType *sizeRet)
{
    uint32 eraseSize = 0u;

    if (hostCtxPtr->mramHost.wrBufferEnable == FALSE)
    {

        (void) Mcal_MemClr((void*)(hostCtxPtr->mramHost.config.axiWrBase + addr), size);
        EEP_CLEAN_CACHE(hostCtxPtr->mramHost.config.axiWrBase + addr, size);
        *sizeRet = size;
    }
    else
    {
        if (SEMAG_E_OK == sdrv_semag_trylock(hostCtxPtr->config->semagID))
        {
            eraseSize = Mram_HostGetWrbufferEntry(&hostCtxPtr->mramHost);
            if (0u != eraseSize)
            {
                eraseSize = MRAM_MIN(eraseSize, size);
                (void) Mcal_MemClr((void*)(hostCtxPtr->mramHost.config.axiWrBase + addr), eraseSize);
                EEP_CLEAN_CACHE(hostCtxPtr->mramHost.config.axiWrBase + addr, eraseSize);

            }
            (void) sdrv_semag_unlock(hostCtxPtr->config->semagID);
        }

        *sizeRet = eraseSize;
    }
    EEP_FAULTINJECT_TRY_ERASE();
}

/** *****************************************************************************************************
 * \brief This function is The actual operation of erase MRAM.
 *
 * \verbatim
 * Syntax             : Std_ReturnType Eep_ErasePrepare(
 *                              Eep_BlockInfoType *pBlock)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Non reentrant
 *
 * Parameters (in)    : pBlock - Point of job's infomation
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : E_OK: Operation successful
 *                      Others: Operation failed
 *
 * Description        : Erase MRAM, Clear to zero
 * \endverbatim
 * Traceability       :
 *******************************************************************************************************/
static Std_ReturnType Eep_ErasePrepare(Eep_BlockInfoType *pBlock)
{
    Std_ReturnType ret = E_OK;
    Eep_HostContextType *hostCtxPtr = NULL_PTR;
    boolean isNeedJumpToPost = FALSE;
    Eep_LengthType eraseSize, eraseSizeTry;
    Eep_AddressType currentStartAddr = pBlock->jobInfo.startAddr + pBlock->jobInfo.hasPrepareDoneBytes;
    Eep_LengthType residualSize = pBlock->jobInfo.length - pBlock->jobInfo.hasPrepareDoneBytes;
    Eep_ContextType *eepContextPtr = &Eep_Context[Mcal_GetCpuIndex()];

    if (eepContextPtr->maxNumBytesWrite > 0u)
    {
        residualSize = MRAM_MIN(eepContextPtr->maxNumBytesWrite, residualSize);
    }

    if (pBlock->mergedWrAxiBaseAddress != 0u)
    {
        /* Direct memclear as a whole */
        (void) Mcal_MemClr(
            (void*)(pBlock->mergedWrAxiBaseAddress + currentStartAddr - pBlock->mergedBaseAddress),
            residualSize);
        EEP_CLEAN_CACHE(pBlock->mergedWrAxiBaseAddress +
            currentStartAddr - pBlock->mergedBaseAddress , residualSize);

        pBlock->jobInfo.hasPrepareDoneBytes += residualSize;
    }
    else
    {
        while ((residualSize > 0u) && (E_OK == ret))
        {
            /* Split the copy, if not on the same MRAM */
            eraseSize = Eep_GetHostContext(pBlock, currentStartAddr, residualSize, &hostCtxPtr);
            if (eraseSize == 0u)
            {
                ret = E_NOT_OK;
                EEP_DEBUG("Can not get host context (0x%x 0x%x)\r\n", currentStartAddr, residualSize);
            }
            else
            {
                /* Transmission length is 'eraseSizeTry' */
                (void) Eep_EraseTry(hostCtxPtr, currentStartAddr - hostCtxPtr->baseAddress,
                        eraseSize, &eraseSizeTry);
                residualSize -= eraseSizeTry;
                currentStartAddr += eraseSizeTry;
                pBlock->jobInfo.hasPrepareDoneBytes += eraseSizeTry;

                /* MRAM is currently blocked and cannot transfer. Go to the next stage */
                if ((eraseSizeTry == 0u) || (eraseSizeTry != eraseSize))
                {
                    isNeedJumpToPost = TRUE;
                    break; /* Immediate jump */
                }

                /* Because turning on wrBuffer, need to jump post later */
                if (hostCtxPtr->mramHost.wrBufferEnable == TRUE)
                {
                    isNeedJumpToPost = TRUE;
                }
            }
        }
    }

    if (isNeedJumpToPost == FALSE)
    {
        pBlock->jobInfo.hasDoneBytes = pBlock->jobInfo.hasPrepareDoneBytes;
    }
    else
    {
        /* MRAM is currently blocked and cannot transfer. Go to the next stage */
        pBlock->jobInfo.jobStatus = EEP_JOB_POST;
    }
    return ret;
}


/** *****************************************************************************************************
 * \brief This function is Refresh the Cache buffer for mram.
 *
 * \verbatim
 * Syntax             : Std_ReturnType Eep_RefreshPost(
 *                              Eep_BlockInfoType *pBlock)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Non reentrant
 *
 * Parameters (in)    : pBlock - Point of job's infomation
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : E_OK: Operation successful
 *                      Others: Operation failed
 *
 * Description        : Refresh the Cache buffer for MRAM
 * \endverbatim
 * Traceability       :
 *******************************************************************************************************/
static Std_ReturnType Eep_RefreshPost(Eep_BlockInfoType *pBlock)
{
    Std_ReturnType ret = E_OK;
    Eep_HostContextType *hostCtxPtr = NULL_PTR;
    Eep_LengthType refreshSize;
    Eep_AddressType currentStartAddr = pBlock->jobInfo.startAddr + pBlock->jobInfo.hasDoneBytes;

    while ((pBlock->jobInfo.hasPrepareDoneBytes > pBlock->jobInfo.hasDoneBytes) && (E_OK == ret))
    {
        refreshSize = Eep_GetHostContext(pBlock, currentStartAddr,
                pBlock->jobInfo.hasPrepareDoneBytes - pBlock->jobInfo.hasDoneBytes, &hostCtxPtr);
        if (refreshSize == 0u)
        {
            ret = E_NOT_OK;
            EEP_DEBUG("Can not get host context (0x%x)\r\n", currentStartAddr);
        }
        else
        {
            (void) Mram_HostWrBufferFlush(&hostCtxPtr->mramHost, MRAM_FLUSH_ONLY_CURRENT);
            currentStartAddr += refreshSize;
            pBlock->jobInfo.hasDoneBytes += refreshSize;
        }
    }
    pBlock->jobInfo.jobStatus = EEP_JOB_PREPARE;
    return ret;
}

/** *****************************************************************************************************
 * \brief This function is The actual operation of Erase memory.
 *
 * \verbatim
 * Syntax             : Eep_ExtendedProductionErrorType Eep_EraseProcess(
 *                              Eep_BlockInfoType *pBlock)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Non reentrant
 *
 * Parameters (in)    : pJob - Point of job's infomation
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : EEP_E_NO_ERROR: Operation successful
 *                      Others: Operation failed
 *
 * Description        : Erase mram memory
 * \endverbatim
 * Traceability       :
 *******************************************************************************************************/
static Eep_ExtendedProductionErrorType  Eep_EraseProcess(Eep_BlockInfoType *pBlock)
{
    Eep_ExtendedProductionErrorType stRet = EEP_E_NO_ERROR;

    if (EEP_JOB_PREPARE == pBlock->jobInfo.jobStatus)
    {
        if (E_OK != Eep_ErasePrepare(pBlock))
        {
            stRet = EEP_E_ERASE_FAILED;
        }
    }
    else
    {
        if (E_OK != Eep_RefreshPost(pBlock))
        {
            stRet = EEP_E_ERASE_FAILED;
        }
    }

    if (stRet != EEP_E_NO_ERROR)
    {
        EEP_DET_REPORT_TRANS_FAULT(EEP_MODULE_ID, 0, (uint8)EEP_ERASE_SERVICE_ID, EEP_E_ERASE_FAILED);
    }

    return stRet;
}

/** *****************************************************************************************************
 * \brief This function is The actual operation of reading data.
 *
 * \verbatim
 * Syntax             : Eep_ExtendedProductionErrorType Eep_ReadProcess(
 *                              Eep_BlockInfoType *pBlock)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Non reentrant
 *
 * Parameters (in)    : pJob - Point of job's infomation
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : EEP_E_NO_ERROR: Operation successful
 *                      Others: Operation failed
 *
 * Description        : Read data from mram
 * \endverbatim
 * Traceability       :
 *******************************************************************************************************/
static Eep_ExtendedProductionErrorType  Eep_ReadProcess(Eep_BlockInfoType *pBlock)
{
    Eep_HostContextType *hostCtxPtr = NULL_PTR;
    Eep_LengthType readSize;
    Eep_ExtendedProductionErrorType stRet = EEP_E_NO_ERROR;
    Eep_AddressType currentStartAddr = pBlock->jobInfo.startAddr + pBlock->jobInfo.hasDoneBytes;
    uint8 *destBuffer = &pBlock->jobInfo.destPtr[pBlock->jobInfo.hasDoneBytes];
    Eep_LengthType residualSize = pBlock->jobInfo.length - pBlock->jobInfo.hasDoneBytes;
    Eep_ContextType *eepContextPtr = &Eep_Context[Mcal_GetCpuIndex()];

    if (eepContextPtr->maxNumBytesRead > 0u)
    {
        residualSize = MRAM_MIN(eepContextPtr->maxNumBytesRead, residualSize);
    }

    if (pBlock->mergedRdAxiBaseAddress != 0u)
    {
        /* Direct memcopy as a whole */
        EEP_INVALIDATE_CACHE(pBlock->mergedRdAxiBaseAddress + currentStartAddr -
                            pBlock->mergedBaseAddress, residualSize);

        (void) Mram_HostMemcopy((void*)destBuffer,
            (void*)(pBlock->mergedRdAxiBaseAddress + currentStartAddr - pBlock->mergedBaseAddress ),
            residualSize);

        pBlock->jobInfo.hasDoneBytes += residualSize;
    }
    else
    {
        /* Split the copy, if not on the same MRAM */
        readSize = Eep_GetHostContext(pBlock, currentStartAddr, residualSize, &hostCtxPtr);
        if (readSize == 0u)
        {
            stRet = EEP_E_READ_FAILED;
            EEP_DEBUG("Can not get host context (0x%x 0x%x)\r\n", currentStartAddr, residualSize);
        }
        else
        {
            (void) Mram_HostRdBufferInvalidate(&hostCtxPtr->mramHost);

            EEP_INVALIDATE_CACHE(hostCtxPtr->mramHost.config.axiRdBase +
                        currentStartAddr - hostCtxPtr->baseAddress, readSize);

            if (MRAM_RET_OK != Mram_HostRead(&hostCtxPtr->mramHost,
                    currentStartAddr - hostCtxPtr->baseAddress, destBuffer, readSize))
            {
                stRet = EEP_E_READ_FAILED;
                EEP_DEBUG("Host read failed (0x%x 0x%x)\r\n", currentStartAddr, readSize);
            }
            else
            {
                pBlock->jobInfo.hasDoneBytes += readSize;
            }
        }
    }

    if (stRet != EEP_E_NO_ERROR)
    {
        EEP_DET_REPORT_TRANS_FAULT(EEP_MODULE_ID, 0, (uint8)EEP_READ_SERVICE_ID, EEP_E_READ_FAILED);
    }

    return stRet;
}

static Std_ReturnType Eep_WriteTry(Eep_HostContextType *hostCtxPtr, Eep_AddressType addr,
    const uint8 *buffer, Eep_LengthType size, Eep_LengthType *sizeRet)
{
    Std_ReturnType ret = E_OK;
    uint32 writeSize = 0u;

    if (hostCtxPtr->mramHost.wrBufferEnable == FALSE)
    {
        if (MRAM_RET_OK != Mram_HostWrite(&hostCtxPtr->mramHost, addr, buffer, size))
        {
            ret = E_NOT_OK;
        }
        else
        {
            writeSize = size;
            EEP_CLEAN_CACHE(hostCtxPtr->mramHost.config.axiWrBase + addr, writeSize);
        }
    }
    else
    {
        if (SEMAG_E_OK == sdrv_semag_trylock(hostCtxPtr->config->semagID))
        {
            writeSize = Mram_HostGetWrbufferEntry(&hostCtxPtr->mramHost);
            if (0u != writeSize)
            {
                writeSize = MRAM_MIN(writeSize, size);
                if (MRAM_RET_OK != Mram_HostWrite(&hostCtxPtr->mramHost, addr, buffer, writeSize))
                {
                    ret = E_NOT_OK;
                    writeSize = 0u;
                }
                else
                {
                    EEP_CLEAN_CACHE(hostCtxPtr->mramHost.config.axiWrBase + addr, writeSize);
                }
            }

            (void) sdrv_semag_unlock(hostCtxPtr->config->semagID);
        }
    }

    *sizeRet = writeSize;
    EEP_FAULTINJECT_TRY_WRITE();
    return ret;
}

/** *****************************************************************************************************
 * \brief This function is The actual operation of writing data.
 *
 * \verbatim
 * Syntax             : Std_ReturnType Eep_WritePrepare(
 *                              Eep_BlockInfoType *pBlock)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Non reentrant
 *
 * Parameters (in)    : pBlock - Point of job's infomation
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : E_OK: Operation successful
 *                      Others: Operation failed
 *
 * Description        : Write data to Mram
 * \endverbatim
 * Traceability       :
 *******************************************************************************************************/
static Std_ReturnType Eep_WritePrepare(Eep_BlockInfoType *pBlock)
{
    Std_ReturnType ret = E_OK;
    Eep_HostContextType *hostCtxPtr = NULL_PTR;
    boolean isNeedJumpToPost = FALSE;
    Eep_LengthType writeSize, writeSizeTry;
    Eep_AddressType currentStartAddr = pBlock->jobInfo.startAddr + pBlock->jobInfo.hasPrepareDoneBytes;
    const uint8 *sourceBuffer = &pBlock->jobInfo.sourcePtr[pBlock->jobInfo.hasPrepareDoneBytes];
    Eep_LengthType residualSize = pBlock->jobInfo.length - pBlock->jobInfo.hasPrepareDoneBytes;
    Eep_ContextType *eepContextPtr = &Eep_Context[Mcal_GetCpuIndex()];

    if (eepContextPtr->maxNumBytesWrite > 0u)
    {
        residualSize = MRAM_MIN(eepContextPtr->maxNumBytesWrite, residualSize);
    }

    if (pBlock->mergedWrAxiBaseAddress != 0u)
    {
        /* Direct memcopy as a whole */
        (void) Mram_HostMemcopy(
            (void*)(pBlock->mergedWrAxiBaseAddress + currentStartAddr - pBlock->mergedBaseAddress),
            (const void*)sourceBuffer, residualSize);
        EEP_CLEAN_CACHE(pBlock->mergedWrAxiBaseAddress +
            currentStartAddr - pBlock->mergedBaseAddress, residualSize);

        pBlock->jobInfo.hasPrepareDoneBytes += residualSize;
    }
    else
    {
        while ((residualSize > 0u) && (E_OK == ret))
        {
            /* Split the copy, if not on the same MRAM */
            writeSize = Eep_GetHostContext(pBlock, currentStartAddr, residualSize, &hostCtxPtr);
            if (writeSize == 0u)
            {
                ret = E_NOT_OK;
                EEP_DEBUG("Can not get host context (0x%x 0x%x)\r\n", currentStartAddr, residualSize);
            }
            else
            {
                /* Transmission length is 'writeSizeTry' */
                (void) Eep_WriteTry(hostCtxPtr, currentStartAddr - hostCtxPtr->baseAddress,
                        sourceBuffer, writeSize, &writeSizeTry);
                residualSize -= writeSizeTry;
                currentStartAddr += writeSizeTry;
                sourceBuffer += writeSizeTry;
                pBlock->jobInfo.hasPrepareDoneBytes += writeSizeTry;

                /* MRAM is currently blocked and cannot transfer. Go to the next stage */
                if ((writeSizeTry == 0u) || (writeSizeTry != writeSize))
                {
                    isNeedJumpToPost = TRUE;
                    break; /* Immediate jump */
                }

                /* Because turning on wrBuffer, need to jump post later */
                if (hostCtxPtr->mramHost.wrBufferEnable == TRUE)
                {
                    isNeedJumpToPost = TRUE;
                }
            }
        }
    }

    if (isNeedJumpToPost == FALSE)
    {
        pBlock->jobInfo.hasDoneBytes = pBlock->jobInfo.hasPrepareDoneBytes;
    }
    else
    {
        /* MRAM is currently blocked and cannot transfer. Go to the next stage */
        pBlock->jobInfo.jobStatus = EEP_JOB_POST;
    }
    return ret;
}

/** *****************************************************************************************************
 * \brief This function is The flow of writing data.
 *
 * \verbatim
 * Syntax             : Eep_ExtendedProductionErrorType Eep_WriteProcess(
 *                              Eep_BlockInfoType *pBlock)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Non reentrant
 *
 * Parameters (in)    : pJob - Point of job's infomation
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : EEP_E_NO_ERROR: Operation successful
 *                      Others: Operation failed
 *
 * Description        : Write data to MRAM
 * \endverbatim
 * Traceability       :
 *******************************************************************************************************/
static Eep_ExtendedProductionErrorType  Eep_WriteProcess(Eep_BlockInfoType *pBlock)
{
    Eep_ExtendedProductionErrorType stRet = EEP_E_NO_ERROR;

    if (EEP_JOB_PREPARE == pBlock->jobInfo.jobStatus)
    {
        if (E_OK != Eep_WritePrepare(pBlock))
        {
            stRet = EEP_E_WRITE_FAILED;
        }
    }
    else
    {
        if (E_OK != Eep_RefreshPost(pBlock))
        {
            stRet = EEP_E_WRITE_FAILED;
        }
    }

    if (stRet != EEP_E_NO_ERROR)
    {
        EEP_DET_REPORT_TRANS_FAULT(EEP_MODULE_ID, 0, (uint8)EEP_WRITE_SERVICE_ID, EEP_E_WRITE_FAILED);
    }

    return stRet;
}

#if (EEP_CALL_CYCLES != 0u)
/** *****************************************************************************************************
 * \brief This function Check whether the current operation is timeout
 *
 * \verbatim
 * Syntax             : uint8 Eep_TimeOut(
 *                          Eep_BlockInfoType *pBlock)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Non reentrant
 *
 * Parameters (in)    : pBlock - Point of job's information
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : E_OK: Operation not timed out
 *                      E_NOT_OK: Operation timed out
 *
 * Description        : Check whether the current operation is timeout
 * \endverbatim
 * Traceability       :
 *******************************************************************************************************/
static uint8 Eep_TimeOut(Eep_BlockInfoType *pBlock)
{
    uint8 errorFlag = E_OK;

    if (TRUE == pBlock->jobInfo.timeoutCheckEnable)
    {

        if (pBlock->jobInfo.timeoutCycleCount > 0U)
        {
            pBlock->jobInfo.timeoutCycleCount--;
        }
        else
        {
            errorFlag = E_NOT_OK;
        }

        if (errorFlag == E_NOT_OK)
        {
            EEP_DET_REPORT_TRANS_FAULT(EEP_MODULE_ID, 0, (uint8)pBlock->jobInfo.serviceId, EEP_E_TIMEOUT);
        }
    }

    return errorFlag;
}
#endif /* #if (EEP_CALL_CYCLES != 0u) */

/** *****************************************************************************************************
 * \brief This function Processing a pending job
 *
 * \verbatim
 * Syntax             : void Eep_DoPendingJob (
 *                                  Eep_JobInfoType* *pJob)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Non reentrant
 *
 * Parameters (in)    : pJob - Point of job's information
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : Point of unfinished job
 *
 * Description        : Processing a pending job
 * \endverbatim
 * Traceability       :
 *******************************************************************************************************/
static void Eep_DoPendingJob(Eep_BlockInfoType *pBlock)
{
    Eep_ExtendedProductionErrorType stRet = EEP_E_EXT_PRODUCT_ERROR_MAX;

    if (NULL_PTR == pBlock)
    {
        EEP_DEBUG("Pending job is null\n");
    }
#if (EEP_CALL_CYCLES != 0u)
    else if (E_OK != Eep_TimeOut(pBlock))
    {
        /* Timeout */
        stRet = EEP_E_OPERATION_TIMEOUT;
    }
#endif /* #if (EEP_CALL_CYCLES != 0u) */
    else if (MEMIF_JOB_PENDING == pBlock->jobInfo.jobResult)
    {
        EEP_DEBUG("Do 0x%x job (%d %d)\n", &pBlock->node, pBlock->jobInfo.serviceId, pBlock->jobInfo.hasDoneBytes);
        switch (pBlock->jobInfo.serviceId)
        {
        case EEP_WRITE_SERVICE_ID:
        {
            stRet =  Eep_WriteProcess(pBlock);
            break;
        }
        case EEP_ERASE_SERVICE_ID:
        {
            stRet =  Eep_EraseProcess(pBlock);
            break;
        }
        case EEP_COMPARE_SERVICE_ID:
        {
            stRet =  Eep_CompareProcess(pBlock);
            break;
        }
        case EEP_READ_SERVICE_ID:
        {
            stRet =  Eep_ReadProcess(pBlock);
            break;
        }
        default:
            EEP_DEBUG("Job(0x%x) unkown service id:%d\n", &pBlock->node, pBlock->jobInfo.serviceId);
            break;
        }
    }
    else if (MEMIF_JOB_CANCELED == pBlock->jobInfo.jobResult)
    {
        stRet = EEP_E_OPERATION_CANCEL;
        EEP_DEBUG("The job(0x%x) has been cancel\r\n", &pBlock->node);
    }
    else
    {
        Eep_ResetOrders(pBlock);
    }

    if (EEP_E_EXT_PRODUCT_ERROR_MAX == stRet)
    {
        /* Do nothing */
    }
    else if ((EEP_E_NO_ERROR != stRet) ||
             (pBlock->jobInfo.hasDoneBytes == pBlock->jobInfo.length))
    {
        Eep_JobEnd(pBlock, stRet); /* The job has been completed */
    }
    else
    {
        Eep_JobInsert(pBlock); /* The job needs to be continued */
    }
}

static uint8 Eep_PreInit(const Eep_ConfigType* ConfigPtr)
{
    uint8 ret = E_OK;
    uint8 i;
    Eep_ContextType *eepContextPtr = &Eep_Context[Mcal_GetCpuIndex()];

    if (NULL_PTR == ConfigPtr)
    {
        ret = EEP_E_PARAM_POINTER;
    }
    else if (TRUE == Eep_IsJobsPending())
    {
        ret = EEP_E_BUSY;
    }
    else
    {
        /* Do nothing */
    }


    if (E_OK == ret)
    {
        SchM_Enter_Eep_EEP_EXCLUSIVE_AREA_00();
        (void) Mcal_MemClr((void*)eepContextPtr, sizeof(Eep_ContextType));
        SchM_Exit_Eep_EEP_EXCLUSIVE_AREA_00();

        for (i = 0; i < EEP_CFG_BLOCK_PRIORITY_MAX; i++)
        {
            Eep_ListInitialize(&eepContextPtr->pendingJobList[i]);
        }

    }

    return ret;
}

static uint8 Eep_ReadBufferSetup(Eep_HostContextType *hostCtxPtr,
    const Mram_RdBufferCfgType *configs, uint8 cfgNum)
{
    uint8 ret = E_OK;

    if (cfgNum > 0u)
    {
        if (0 != Mram_HostRdBufferSetup(&hostCtxPtr->mramHost, configs, cfgNum))
        {
            ret = E_NOT_OK;
        }
    }

    return ret;
}

static void Eep_PostInit(const Eep_ConfigType* ConfigPtr)
{
    Eep_BlockInfoType *pBlock;
    uint8 i;
    Eep_ContextType *eepContextPtr = &Eep_Context[Mcal_GetCpuIndex()];

    for (i = 0; i < EEP_CFG_INIT_BLOCK_NUM; i++)
    {
        pBlock = &eepContextPtr->blockInfo[i];
        pBlock->jobInfo.status = MEMIF_IDLE;
        pBlock->jobInfo.serviceId = EEP_SERVICE_ID_MAX;
    }

#if (EEP_USE_INTERRUPTS == STD_ON)
    for (i = 0; i < EEP_CFG_HOST_MAX_NUM; i++)
    {
        Eep_IrqEventSetup(i, &eepContextPtr->hostCtx[i]);
    }
#endif /* #if (EEP_USE_INTERRUPTS == STD_ON) */

#if (EEP_CFG_IMAGE_AREA_NUM)
    for (i = 0; i < EEP_CFG_IMAGE_AREA_NUM; i++)
    {
        (void) Mcal_MemClr((void*)&Eep_ImageInfo[i], sizeof(EepImageInfoType));
        (void)Mram_HostMemcopy((void*)&Eep_ImageInfo[i].area, (const void*)&ConfigPtr->initConfig.imageArea[i],
            sizeof(Eep_AreaInfoType));
        Eep_ImageInfo[i].crcExpval = EEP_CRC_AUTO_ACQUISITION;
        if (E_OK != Eep_ImageCrcConfig(&Eep_ImageInfo[i]))
        {
            break;
        }
    }
#endif /* #if (EEP_CFG_IMAGE_AREA_NUM) */

    eepContextPtr->memIfMode = ConfigPtr->initConfig.defaultMode;
    eepContextPtr->maxNumBytesWrite = (ConfigPtr->initConfig.defaultMode == MEMIF_MODE_SLOW) ?
        ConfigPtr->initConfig.normalWriteBlockSize :  ConfigPtr->initConfig.fastWriteBlockSize;
    eepContextPtr->maxNumBytesRead = (ConfigPtr->initConfig.defaultMode == MEMIF_MODE_SLOW) ?
        ConfigPtr->initConfig.normalReadBlockSize :  ConfigPtr->initConfig.fastReadBlockSize;
    /* This step indicates that the initialization is finally complete and must be put to the last step */
    eepContextPtr->configPtr = ConfigPtr;

#if (EEP_CFG_PROTECT_AREA_NUM)
    for (i = 0; i < EEP_CFG_PROTECT_AREA_NUM; i++)
    {
        if (E_OK != Eep_Protect(ConfigPtr->initConfig.protectArea[i].address,
                                ConfigPtr->initConfig.protectArea[i].size, true))
        {
            break;
        }
    }
#endif /* #if (EEP_CFG_PROTECT_AREA_NUM) */
}

static uint8 Eep_BlocksInit(const Eep_ConfigType* ConfigPtr)
{
    uint8 ret = E_OK;
    uint8 i, j, indexTemp;
    Eep_BlockInfoType *blockInfo;
    Eep_HostContextType *hostCtx;
    Eep_ContextType *eepContextPtr = &Eep_Context[Mcal_GetCpuIndex()];

    for (i = 0; (ret == E_OK) && (i < EEP_CFG_INIT_BLOCK_NUM); i++)
    {
        blockInfo = &eepContextPtr->blockInfo[i];
        blockInfo->hostCtxTable = NULL_PTR;
        blockInfo->hostNum = 0;
        blockInfo->blockConfig = &ConfigPtr->initConfig.blockConfig[i];
        indexTemp = 0u;
        /* Blockinfo bind host context */
        for (j = 0u; j < EEP_CFG_HOST_MAX_NUM; j++)
        {
            hostCtx = &eepContextPtr->hostCtx[j];
            if ((blockInfo->hostCtxTable == NULL_PTR)
                && (blockInfo->blockConfig->baseAddress >= hostCtx->baseAddress)
                && (blockInfo->blockConfig->baseAddress < (hostCtx->baseAddress + hostCtx->size)))
            {
                /* Find the first host handle */
                blockInfo->hostCtxTable = hostCtx;
                indexTemp = j;
            }

            if ((blockInfo->hostCtxTable != NULL_PTR)
                && ((blockInfo->blockConfig->baseAddress + blockInfo->blockConfig->size)
                    <= (hostCtx->baseAddress + hostCtx->size)))
            {
                /* Find the last host handle */
                blockInfo->hostNum = j - indexTemp + 1u;
                EEP_DEBUG("Block[%d] bind controller[%d:%d]\r\n", i, indexTemp, j);
                break;
            }
        }

        if (blockInfo->hostCtxTable == NULL_PTR)
        {
            /* The start address of the block is out of bounds. */
            ret = EEP_E_PARAM_ADDRESS;
            EEP_DEBUG("Block[%d] bind host failed: %d\r\n", i, ret);
        }
        else if (0u == blockInfo->hostNum)
        {
            /* The length of the block is out of bounds */
            ret = EEP_E_PARAM_LENGTH;
            EEP_DEBUG("Block[%d] bind host failed: %d\r\n", i, ret);
        }
        else
        {
            /* Try to merged host context */
            blockInfo->mergedBaseAddress = blockInfo->hostCtxTable[0].baseAddress;
            blockInfo->mergedRdAxiBaseAddress =
                (blockInfo->hostCtxTable[0].config->rdBufferConfigsNum > 0u) ?
                    0u : blockInfo->hostCtxTable[0].mramHost.config.axiRdBase;
            blockInfo->mergedWrAxiBaseAddress =
                (blockInfo->hostCtxTable[0].mramHost.wrBufferEnable != FALSE) ?
                    0u : blockInfo->hostCtxTable[0].mramHost.config.axiWrBase;
            for (j = 0; j < (blockInfo->hostNum - 1u); j++)
            {
                if ((0u != blockInfo->mergedRdAxiBaseAddress) &&
                    (((blockInfo->hostCtxTable[j].mramHost.config.axiRdBase +
                    blockInfo->hostCtxTable[j].size) !=
                    blockInfo->hostCtxTable[j + 1u].mramHost.config.axiRdBase) ||
                    (blockInfo->hostCtxTable[j + 1u].config->rdBufferConfigsNum > 0u)))
                {
                    /* The read base address is discontinuous, Cannot merge */
                    blockInfo->mergedRdAxiBaseAddress = 0u;
                }
                if ((0u != blockInfo->mergedWrAxiBaseAddress) &&
                    (((blockInfo->hostCtxTable[j].mramHost.config.axiWrBase +
                    blockInfo->hostCtxTable[j].size) !=
                    blockInfo->hostCtxTable[j + 1u].mramHost.config.axiWrBase) ||
                    (FALSE != blockInfo->hostCtxTable[j + 1u].mramHost.wrBufferEnable)))
                {
                    /* The write base address is discontinuous, Cannot merge */
                    blockInfo->mergedWrAxiBaseAddress = 0u;
                }
            }
            EEP_DEBUG("Block[%d] Addr:0x%x Base(0x%x 0x%x)\r\n",
                i, blockInfo->mergedBaseAddress, blockInfo->mergedRdAxiBaseAddress,
                blockInfo->mergedWrAxiBaseAddress);
        }
    }

    return ret;
}

/********************************************************************************************************
 *                                  Global Function Declarations                                        *
 *******************************************************************************************************/

void Eep_Init(const Eep_ConfigType* ConfigPtr)
{
    uint8 i;
    Eep_ContextType *eepContextPtr = &Eep_Context[Mcal_GetCpuIndex()];
    uint8 ret = Eep_PreInit(ConfigPtr);
    Mram_HostConfigType hostConfigTemp;

    if (E_OK != ret)
    {
        EEP_DET_REPORT_ERROR(EEP_MODULE_ID, 0, (uint8)EEP_INIT_SERVICE_ID, ret);
    }
    else
    {
        /* Hardware level Hosts controller init */
        for (i = 0; (E_OK == ret) && (i < EEP_CFG_HOST_MAX_NUM); i++)
        {
            (void) Mcal_MemCpy((void*)&hostConfigTemp,
                                (const void*)&ConfigPtr->controllerConfig[i].hostConfig,
                                sizeof(Mram_HostConfigType));
            if (Mcal_GetCpuIndex() != CPU_INDEX_0)
            {
                hostConfigTemp.config.initialMode = MRAM_INIT_BYPASS;
            }
            if (MRAM_RET_OK != Mram_HostInit(&eepContextPtr->hostCtx[i].mramHost, &hostConfigTemp))
            {
                ret = E_NOT_OK;
                EEP_DEBUG("Host[%d] init failed\r\n", i);
            }
            else
            {
                eepContextPtr->hostCtx[i].config = &ConfigPtr->controllerConfig[i];
                eepContextPtr->hostCtx[i].baseAddress = eepContextPtr->totalSize;
                eepContextPtr->hostCtx[i].mramHost.index = i;
                eepContextPtr->hostCtx[i].mramHost.base = eepContextPtr->totalSize;
                eepContextPtr->hostCtx[i].size = eepContextPtr->hostCtx[i].mramHost.info.totalSize;
                eepContextPtr->totalSize += eepContextPtr->hostCtx[i].size;

                if (E_OK != Eep_ReadBufferSetup(&eepContextPtr->hostCtx[i],
                        ConfigPtr->controllerConfig[i].rdBufferConfigs,
                        ConfigPtr->controllerConfig[i].rdBufferConfigsNum))
                {
                    ret = E_NOT_OK;
                    EEP_DEBUG("Host[%d] Eep_ReadBufferSetup failed\r\n", i);
                }
                else
                {
                    /* Do nothing */
                }
            }

        }

        /* Software level Blocks init */
        if (E_OK == ret)
        {
            ret = Eep_BlocksInit(ConfigPtr);
        }

        /* Initialization of the context, or recovery */
        if (E_OK != ret)
        {
            EEP_DET_REPORT_ERROR(EEP_MODULE_ID, 1, (uint8)EEP_INIT_SERVICE_ID, ret);
            Eep_Deinit();
        }
        else
        {
            Eep_PostInit(ConfigPtr);
        }
    }
}

/** *****************************************************************************************************
 * \brief This function Sets the driver's operation mode.
 *
 * \verbatim
 * Syntax             : void Eep_SetMode(
 *                          MemIf_ModeType Mode)
 *
 * Service ID[hex]    : 0x01
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Non reentrant
 *
 * Parameters (in)    : Mode - MEMIF_MODE_SLOW: Slow read access / normal SPI access.
 *                             MEMIF_MODE_FAST: Fast read access / SPI burst access.
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : None
 *
 * Description        : Set the EEP module operation mode to the given Mode parameter
 * \endverbatim
 * Traceability       :
 *******************************************************************************************************/
void Eep_SetMode(MemIf_ModeType Mode)
{
    Eep_ContextType *eepContextPtr = &Eep_Context[Mcal_GetCpuIndex()];
    if (NULL_PTR == eepContextPtr->configPtr)
    {
        EEP_DET_REPORT_ERROR(EEP_MODULE_ID, 0, (uint8)EEP_SET_MODE_SERVICE_ID, EEP_E_UNINIT);
    }
    else if (TRUE == Eep_IsJobsPending())
    {
        EEP_DET_REPORT_ERROR(EEP_MODULE_ID, 0, (uint8)EEP_SET_MODE_SERVICE_ID, EEP_E_BUSY);
    }
    else
    {
        eepContextPtr->memIfMode = Mode;
        eepContextPtr->maxNumBytesWrite = (Mode == MEMIF_MODE_SLOW) ?
            eepContextPtr->configPtr->initConfig.normalWriteBlockSize :
            eepContextPtr->configPtr->initConfig.fastWriteBlockSize;
        eepContextPtr->maxNumBytesRead = (Mode == MEMIF_MODE_SLOW) ?
            eepContextPtr->configPtr->initConfig.normalReadBlockSize :
            eepContextPtr->configPtr->initConfig.fastReadBlockSize;
    }
}


/** *****************************************************************************************************
 * \brief This function read from mram memory..
 *
 * \verbatim
 * Syntax             : Std_ReturnType Eep_Read(
 *                          Eep_AddressType EepromAddress, const uint8 *DataBufferPtr,
 *                          Eep_LengthType Length)
 *
 * Service ID[hex]    : 0x02
 *
 * Sync/Async         : Asynchronous
 *
 * Reentrancy         : Non reentrant
 *
 * Parameters (in)    : EepromAddress - Target address in mram memory.
 *                      DataBufferPtr - Pointer to data buffer
 *                      Length - Number of bytes to read
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : E_OK - Oprational command has been accepted
 *                      E_NOT_OK - Oprational command has not been
 *
 * Description        : Reads from mram memory.
 * \endverbatim
 * Traceability       :
 *******************************************************************************************************/
Std_ReturnType Eep_Read(Eep_AddressType EepromAddress, uint8 *DataBufferPtr,
                        Eep_LengthType Length)
{
    Std_ReturnType ret = E_NOT_OK;
    Eep_BlockInfoType *pBlock;

    if (NULL_PTR == DataBufferPtr)
    {
        EEP_DET_REPORT_ERROR(EEP_MODULE_ID, 0, (uint8)EEP_READ_SERVICE_ID, EEP_E_PARAM_DATA);
    }
    else
    {
        pBlock = Eep_JobPrepare(EepromAddress, Length, EEP_READ_SERVICE_ID);

        if (NULL_PTR != pBlock)
        {
            ret = E_OK;
            pBlock->jobInfo.destPtr = DataBufferPtr;
            Eep_JobInsert(pBlock);
        }
    }

    return ret;
}

/** *****************************************************************************************************
 * \brief This function write to mram memory.
 *
 * \verbatim
 * Syntax             : Std_ReturnType Eep_Write(
 *                          Eep_AddressType EepromAddress, const uint8 *DataBufferPtr,
 *                          Eep_LengthType Length)
 *
 * Service ID[hex]    : 0x03
 *
 * Sync/Async         : Asynchronous
 *
 * Reentrancy         : Non reentrant
 *
 * Parameters (in)    : EepromAddress - Target address in mram memory.
 *                      DataBufferPtr - Pointer to data buffer
 *                      Length - Number of bytes to write
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : E_OK - Oprational command has been accepted
 *                      E_NOT_OK - Oprational command has not been
 *
 * Description        : writes to mram memory.
 * \endverbatim
 * Traceability       :
 *******************************************************************************************************/
Std_ReturnType Eep_Write(Eep_AddressType EepromAddress, const uint8* DataBufferPtr,
                            Eep_LengthType Length)
{
    Std_ReturnType ret = E_NOT_OK;
    Eep_BlockInfoType *pBlock;
#if (EEP_CALL_CYCLES != 0u)
    Eep_ContextType *eepContextPtr = &Eep_Context[Mcal_GetCpuIndex()];
#endif /* #if (EEP_CALL_CYCLES != 0u) */

    if (NULL_PTR == DataBufferPtr)
    {
        EEP_DET_REPORT_ERROR(EEP_MODULE_ID, 0, (uint8)EEP_WRITE_SERVICE_ID, EEP_E_PARAM_DATA);
    }
    else
    {
        pBlock = Eep_JobPrepare(EepromAddress, Length, EEP_WRITE_SERVICE_ID);

        if (NULL_PTR != pBlock)
        {
            ret = E_OK;
            pBlock->jobInfo.sourcePtr = DataBufferPtr;
#if (EEP_CALL_CYCLES != 0u)
        pBlock->jobInfo.timeoutCycleCount = (eepContextPtr->maxNumBytesWrite > 0u) ?
            MRAM_DIV_ROUND_UP(Length, eepContextPtr->maxNumBytesWrite) : 1u;
        if (pBlock->mergedWrAxiBaseAddress == 0u)
        {
            pBlock->jobInfo.timeoutCycleCount = (pBlock->jobInfo.timeoutCycleCount + 4u) << 1;
        }
        /* The number of timeouts calculated based on the average rate */
        pBlock->jobInfo.timeoutCycleCount =
            MRAM_MAX(pBlock->jobInfo.timeoutCycleCount,
                MRAM_DIV_ROUND_UP(
                    (MRAM_DIV_ROUND_UP(Length, EEP_WRITE_UNIT_SIZE)
                        * EEP_WRITE_TIME), EEP_CALL_CYCLES));
        pBlock->jobInfo.timeoutCheckEnable = TRUE;
#endif /* #if (EEP_CALL_CYCLES != 0u) */
            Eep_JobInsert(pBlock);
        }
    }

    return ret;
}

/** *****************************************************************************************************
 * \brief This function Erase mram memory.
 *
 * \verbatim
 * Syntax             : Std_ReturnType Eep_Erase(
 *                          Eep_AddressType EepromAddress, Eep_LengthType Length)
 *
 * Service ID[hex]    : 0x04
 *
 * Sync/Async         : Asynchronous
 *
 * Reentrancy         : Non reentrant
 *
 * Parameters (in)    : EepromAddress - Target address in mram memory.
 *                      DataBufferPtr - Pointer to data buffer
 *                      Length - Number of bytes to write
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : E_OK - Oprational command has been accepted
 *                      E_NOT_OK - Oprational command has not been
 *
 * Description        : Erase mram memory.
 * \endverbatim
 * Traceability       :
 *******************************************************************************************************/
Std_ReturnType Eep_Erase(Eep_AddressType EepromAddress, Eep_LengthType Length)
{
    Std_ReturnType ret = E_NOT_OK;
    Eep_BlockInfoType *pBlock = Eep_JobPrepare(EepromAddress, Length, EEP_ERASE_SERVICE_ID);
#if (EEP_CALL_CYCLES != 0u)
    Eep_ContextType *eepContextPtr = &Eep_Context[Mcal_GetCpuIndex()];
#endif /* #if (EEP_CALL_CYCLES != 0u) */

    if (NULL_PTR != pBlock)
    {
        ret = E_OK;
#if (EEP_CALL_CYCLES != 0u)
        pBlock->jobInfo.timeoutCycleCount = (eepContextPtr->maxNumBytesWrite > 0u) ?
            MRAM_DIV_ROUND_UP(Length, eepContextPtr->maxNumBytesWrite) : 1u;
        if (pBlock->mergedWrAxiBaseAddress == 0u)
        {
            pBlock->jobInfo.timeoutCycleCount = (pBlock->jobInfo.timeoutCycleCount + 4u) << 1;
        }
        /* The number of timeouts calculated based on the average rate */
        pBlock->jobInfo.timeoutCycleCount =
            MRAM_MAX(pBlock->jobInfo.timeoutCycleCount,
                MRAM_DIV_ROUND_UP(
                    (MRAM_DIV_ROUND_UP(Length, EEP_ERASE_UNIT_SIZE)
                        * EEP_ERASE_TIME), EEP_CALL_CYCLES));
        pBlock->jobInfo.timeoutCheckEnable = TRUE;
#endif /* #if (EEP_CALL_CYCLES != 0u) */
        Eep_JobInsert(pBlock);
    }

    return ret;
}

/** *****************************************************************************************************
 * \brief This function compare between user data and mram memory.
 *
 * \verbatim
 * Syntax             : Std_ReturnType Eep_Compare(
 *                          Eep_AddressType EepromAddress, const uint8 *DataBufferPtr,
 *                          Eep_LengthType Length)
 *
 * Service ID[hex]    : 0x05
 *
 * Sync/Async         : Asynchronous
 *
 * Reentrancy         : Non reentrant
 *
 * Parameters (in)    : EepromAddress - Target address in mram memory.
 *                      DataBufferPtr - Pointer to data buffer
 *                      Length - Number of bytes to read
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : E_OK - Oprational command has been accepted
 *                      E_NOT_OK - Oprational command has not been
 *
 * Description        : compare between user data and mram memory.
 * \endverbatim
 * Traceability       :
 *******************************************************************************************************/
Std_ReturnType Eep_Compare(Eep_AddressType EepromAddress, const uint8 *DataBufferPtr,
                        Eep_LengthType Length)
{
    Std_ReturnType ret = E_NOT_OK;
    Eep_BlockInfoType *pBlock;

    if (NULL_PTR == DataBufferPtr)
    {
        EEP_DET_REPORT_ERROR(EEP_MODULE_ID, 0, (uint8)EEP_COMPARE_SERVICE_ID, EEP_E_PARAM_DATA);
    }
    else
    {
        pBlock = Eep_JobPrepare(EepromAddress, Length, EEP_COMPARE_SERVICE_ID);

        if (NULL_PTR != pBlock)
        {
            ret = E_OK;
            pBlock->jobInfo.sourcePtr = DataBufferPtr;
            Eep_JobInsert(pBlock);
        }
    }

    return ret;
}

void Eep_Cancel(void)
{
    uint8 i;
    Eep_BlockInfoType *pBlock;
    Eep_ContextType *eepContextPtr = &Eep_Context[Mcal_GetCpuIndex()];

    if (NULL_PTR == eepContextPtr->configPtr)
    {
        EEP_DET_REPORT_ERROR(EEP_MODULE_ID, 0, (uint8)EEP_CANCEL_SERVICE_ID, EEP_E_UNINIT);
    }
    else
    {
        for (i = 0; i < EEP_CFG_INIT_BLOCK_NUM; i++)
        {
            pBlock = &eepContextPtr->blockInfo[i];

            if (MEMIF_BUSY == pBlock->jobInfo.status)
            {
                SchM_Enter_Eep_EEP_EXCLUSIVE_AREA_00();
                pBlock->jobInfo.jobResult = MEMIF_JOB_CANCELED;

                if (TRUE == Eep_ListInList(&pBlock->node))
                {
                    /* The current job is waiting */
                    Eep_ListDelete(&pBlock->node);
                    SchM_Exit_Eep_EEP_EXCLUSIVE_AREA_00();

                    /* End the current job directly */
                    Eep_JobEnd(pBlock, EEP_E_OPERATION_CANCEL);
                }
                else
                {
                    /* The current job is being processed and JobEnd will be called in Mainfunction */
                    SchM_Exit_Eep_EEP_EXCLUSIVE_AREA_00();
                }

            }
        }
    }
}

/** *****************************************************************************************************
 * \brief This function returns Returns the driver state.
 *
 * \verbatim
 * Syntax             : MemIf_StatusType Eep_GetStatus(void)
 *
 * Service ID[hex]    : 0x04
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : None
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : Driver state (MemIf_StatusType)
 *
 * Description        : Return the EEP module state synchronously.
 * \endverbatim
 * Traceability       :
 *******************************************************************************************************/
MemIf_StatusType Eep_GetStatus( void )
{
    MemIf_StatusType status;
    Eep_ContextType *eepContextPtr = &Eep_Context[Mcal_GetCpuIndex()];

    if (NULL_PTR == eepContextPtr->configPtr)
    {
        status = MEMIF_UNINIT;
    }
    else if (TRUE == Eep_IsJobsPending())
    {
        status = MEMIF_BUSY;
    }
    else
    {
        status = MEMIF_IDLE;
    }

    return status;
}

/** *****************************************************************************************************
 * \brief This function returns the the result of the Specify job.
 *
 * \verbatim
 * Syntax             : MemIf_JobResultType Eep_GetJobResultById(uint8 index)
 *
 * Service ID[hex]    : 0x05
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : index - The index of block
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : Result of the Specify job(MemIf_JobResultType)
 *
 * Description        : return the result of the Specify job synchronously
 * \endverbatim
 * Traceability       :
 *******************************************************************************************************/
MemIf_JobResultType Eep_GetJobResultById(uint8 index)
{
    MemIf_JobResultType jobResult;
    Eep_ContextType *eepContextPtr = &Eep_Context[Mcal_GetCpuIndex()];

    if (NULL_PTR == eepContextPtr->configPtr)
    {
        jobResult = MEMIF_BLOCK_INVALID;
        EEP_DET_REPORT_ERROR(EEP_MODULE_ID, 0, (uint8)EEP_GET_JOBRESULT_SERVICE_ID, EEP_E_UNINIT);
    }
    else if (index >= EEP_CFG_INIT_BLOCK_NUM)
    {
        jobResult = MEMIF_BLOCK_INVALID;
        EEP_DET_REPORT_ERROR(EEP_MODULE_ID, 0, (uint8)EEP_GET_JOBRESULT_SERVICE_ID, EEP_E_PARAM_DATA);
    }
    else
    {
        jobResult = eepContextPtr->blockInfo[index].jobInfo.jobResult;
    }

    return jobResult;
}

/** *****************************************************************************************************
 * \brief This function returns the the result of the job for block0.
 *
 * \verbatim
 * Syntax             : MemIf_JobResultType Eep_GetJobResult()
 *
 * Service ID[hex]    : 0x05
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : None
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : Result of the Specify job(MemIf_JobResultType)
 *
 * Description        : return the result of the job of block0 synchronously
 * \endverbatim
 * Traceability       :
 *******************************************************************************************************/
MemIf_JobResultType Eep_GetJobResult(void)
{
    return Eep_GetJobResultById(0u);
}

/** *****************************************************************************************************
 * \brief This function returns the the result of the last job.
 *
 * \verbatim
 * Syntax             : MemIf_JobResultType Eep_GetLastJobResult(void)
 *
 * Service ID[hex]    : 0x05
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : None
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : Result of the last job(MemIf_JobResultType)
 *
 * Description        : return the result of the last job synchronously
 * \endverbatim
 * Traceability       :
 *******************************************************************************************************/
MemIf_JobResultType Eep_GetLastJobResult(void)
{
    MemIf_JobResultType jobResult;
    Eep_ContextType *eepContextPtr = &Eep_Context[Mcal_GetCpuIndex()];

    if (NULL_PTR == eepContextPtr->configPtr)
    {
        jobResult = MEMIF_BLOCK_INVALID;
        EEP_DET_REPORT_ERROR(EEP_MODULE_ID, 0, (uint8)EEP_GET_JOBRESULT_SERVICE_ID, EEP_E_UNINIT);
    }
    else if (TRUE == Eep_IsJobsPending())
    {
        jobResult = MEMIF_JOB_PENDING;
    }
    else
    {
        jobResult =  eepContextPtr->lastJobResult;
    }

    return jobResult;
}

/** *****************************************************************************************************
 * \brief This function Performs the processing of jobs.
 *
 * \verbatim
 * Syntax             : void Eep_MainFunction(void)
 *
 * Service ID[hex]    : 0x09
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Non reentrant
 *
 * Parameters (in)    : None
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : None
 *
 * Description        : Performs the processing of jobs.
 * \endverbatim
 * Traceability       :
 *******************************************************************************************************/
void Eep_MainFunction( void )
{
    Eep_ContextType *eepContextPtr = &Eep_Context[Mcal_GetCpuIndex()];
    if (NULL_PTR == eepContextPtr->configPtr)
    {
        EEP_DET_REPORT_ERROR(EEP_MODULE_ID, 0, (uint8)EEP_MAINFUNCTION_SERVICE_ID, EEP_E_UNINIT);
    }
    else
    {
        Eep_DoPendingJob(Eep_GetPendingJob());
    }
}

#if (EEP_VERSIONINFO_API == STD_ON)
/** *****************************************************************************************************
 * \brief This function returns the version information of this module.
 *
 * \verbatim
 * Syntax             : void Eep_GetVersionInfo(
 *                          Std_VersionInfoType *VersioninfoPtr)
 *
 * Service ID[hex]    : 0x10
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : None
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : VersioninfoPtr - Pointer to where to store the version information of this module.
 *
 * Return value       : None
 *
 * Description        : Returns the version information of this module.
 * \endverbatim
 * Traceability       :
 *******************************************************************************************************/
void Eep_GetVersionInfo( Std_VersionInfoType *VersioninfoPtr )
{
    if (VersioninfoPtr == NULL_PTR)
    {
        /* Lin transceiver not initialized*/
        EEP_DET_REPORT_ERROR(EEP_MODULE_ID, 0, (uint8)EEP_GET_VERSION_SERVICE_ID, EEP_E_PARAM_POINTER);
    }
    else
    {
        VersioninfoPtr->vendorID = EEP_SW_VENDOR;
        VersioninfoPtr->moduleID = EEP_MODULE_ID;
        VersioninfoPtr->sw_major_version = EEP_SW_MAJOR_VERSION;
        VersioninfoPtr->sw_minor_version = EEP_SW_MINOR_VERSION;
        VersioninfoPtr->sw_patch_version = EEP_SW_PATCH_VERSION;
    }
}
#endif /* #if (EEP_VERSIONINFO_API == STD_ON) */

Std_ReturnType Eep_ImageCrcConfig(EepImageInfoType *imageInfo)
{
    uint8 ret = E_NOT_OK;
    Eep_LengthType overlapLength;
    Mram_ImageCrcCfgType configTemp;
    Eep_HostContextType *hostCtxPtr = NULL_PTR;

    if (NULL_PTR == imageInfo)
    {
        /* Do nothing */
    }
    else if (NULL_PTR == imageInfo->hostCtx)
    {
        /* The Image area is configured for the first time */
        overlapLength = Eep_GetHostContextByArea(imageInfo->area.address, imageInfo->area.size,
                                                &hostCtxPtr);
        if ((NULL_PTR == hostCtxPtr) || (overlapLength != imageInfo->area.size))
        {
            /* Do nothing */
        }
        else if (MRAM_RET_OK != Mram_HostGetValidImageRgnIndex(&hostCtxPtr->mramHost,
                                                                &imageInfo->channelId))
        {
            /* Do nothing */
        }
        else
        {
            SchM_Enter_Eep_EEP_EXCLUSIVE_AREA_00();
            if (EEP_CRC_AUTO_ACQUISITION == imageInfo->crcExpval)
            {
                EEP_INVALIDATE_CACHE(hostCtxPtr->mramHost.config.axiRdBase + imageInfo->area.address
                                + imageInfo->area.size - hostCtxPtr->baseAddress, 64u);
                (void) Mram_HostMemcopy((void*)&imageInfo->crcExpval,
                        (void*)(hostCtxPtr->mramHost.config.axiRdBase + imageInfo->area.address
                                + imageInfo->area.size - hostCtxPtr->baseAddress), 4U);
            }
            imageInfo->hostCtx = hostCtxPtr;
            SchM_Exit_Eep_EEP_EXCLUSIVE_AREA_00();
        }
    }
    else
    {
        /* You need to set the original configuration to invalid first */
        if (0 != Mram_HostImageCrcCheckStart(&imageInfo->hostCtx->mramHost,
                                imageInfo->channelId, MRAM_IMG_CRC_CHK_INVALID))
        {
            SchM_Enter_Eep_EEP_EXCLUSIVE_AREA_00();
            imageInfo->hostCtx = NULL_PTR;
            SchM_Exit_Eep_EEP_EXCLUSIVE_AREA_00();
        }
    }

    if ((NULL_PTR != imageInfo) && (NULL_PTR != imageInfo->hostCtx))
    {
        SchM_Enter_Eep_EEP_EXCLUSIVE_AREA_00();
        configTemp.index = imageInfo->channelId;
        configTemp.address = imageInfo->area.address - imageInfo->hostCtx->baseAddress;
        configTemp.size = imageInfo->area.size;
        configTemp.crcExpval = imageInfo->crcExpval;
        SchM_Exit_Eep_EEP_EXCLUSIVE_AREA_00();
        configTemp.valid = TRUE;

        if (0 != Mram_HostImageCrcSetup(&imageInfo->hostCtx->mramHost, &configTemp))
        {
            SchM_Enter_Eep_EEP_EXCLUSIVE_AREA_00();
            imageInfo->hostCtx = NULL_PTR;
            SchM_Exit_Eep_EEP_EXCLUSIVE_AREA_00();
        }
        else
        {
            ret = E_OK;
        }
    }
    return ret;
}

Std_ReturnType Eep_ImageCrcCheckStart(EepImageInfoType *imageInfo, Mram_CrcCheckModeType mode)
{
    uint8 ret = E_NOT_OK;

    if (NULL_PTR == imageInfo)
    {
        /* Do nothing */
    }
    else if (NULL_PTR == imageInfo->hostCtx)
    {
        /* Do nothing */
    }
    else if (0 != Mram_HostImageCrcCheckStart(&imageInfo->hostCtx->mramHost,
                                                imageInfo->channelId, mode))
    {
        /* Do nothing */
    }
    else
    {
        if (mode == MRAM_IMG_CRC_CHK_INVALID)
        {
            SchM_Enter_Eep_EEP_EXCLUSIVE_AREA_00();
            imageInfo->hostCtx = NULL_PTR;
            SchM_Exit_Eep_EEP_EXCLUSIVE_AREA_00();
        }

        ret = E_OK;
    }
    return ret;
}

Eep_HostContextType *Eep_GetHostContextById(uint8 controllerId)
{
    Eep_HostContextType *hostCtx = NULL_PTR;
    Eep_ContextType *eepContextPtr = &Eep_Context[Mcal_GetCpuIndex()];

    if (NULL_PTR == eepContextPtr->configPtr)
    {
        /* Do nothing */
    }
    else if (controllerId >= EEP_CFG_HOST_MAX_NUM)
    {
        /* Do nothing */
    }
    else
    {
        hostCtx = &eepContextPtr->hostCtx[controllerId];
    }
    return hostCtx;
}

Std_ReturnType Eep_ClockConfig(Eep_HostContextType *hostCtx, uint32 tmcClk)
{
    uint8 ret = E_NOT_OK;
    if (NULL_PTR == hostCtx)
    {
        /* Do nothing */
    }
    else if (0 != Mram_HostTmcClkSetup(&hostCtx->mramHost, tmcClk))
    {
        /* Do nothing */
    }
    else
    {
        ret = E_OK;
    }
    return ret;
}

Std_ReturnType Eep_Protect(Eep_AddressType address, Eep_LengthType size, boolean enable)
{
    Eep_HostContextType *hostCtxPtr = NULL_PTR;
    Eep_LengthType overlapLength;
    Eep_LengthType protectedSize = 0;
    uint8 ret = E_OK;
    Eep_ContextType *eepContextPtr = &Eep_Context[Mcal_GetCpuIndex()];
    if (NULL_PTR == eepContextPtr->configPtr)
    {
        ret = E_NOT_OK;
    }
    else
    {
        while ((protectedSize < size) && (ret == E_OK))
        {
            overlapLength = Eep_GetHostContextByArea(address + protectedSize,
                                                size - protectedSize, &hostCtxPtr);
            if (NULL_PTR == hostCtxPtr)
            {
                ret = E_NOT_OK;
            }
            else if (MRAM_RET_OK != Mram_HostProtectSetup(&hostCtxPtr->mramHost,
                                    address + protectedSize - hostCtxPtr->baseAddress,
                                    overlapLength, enable))
            {
                ret = E_NOT_OK;
            }
            else
            {
                protectedSize += overlapLength;
            }
        }
    }

    return ret;
}

Std_ReturnType Eep_EccRepair(void)
{
    Std_ReturnType ret = E_OK;
    uint8 controllerId;
    Eep_ContextType *eepContextPtr = &Eep_Context[Mcal_GetCpuIndex()];

    if (NULL_PTR == eepContextPtr->configPtr)
    {
        ret = E_NOT_OK;
    }
    else
    {
        for (controllerId = 0u; (controllerId < EEP_CFG_HOST_MAX_NUM) && (ret == E_OK); controllerId++)
        {
            if (MRAM_RET_OK != Mram_HostEccRepair(&eepContextPtr->hostCtx[controllerId].mramHost))
            {
                ret = E_NOT_OK;
            }
            else
            {
                /* Do nothing */
            }
        }
    }

    return ret;
}

void Eep_Deinit(void)
{
    Eep_ContextType *eepContextPtr = &Eep_Context[Mcal_GetCpuIndex()];
#if (EEP_USE_INTERRUPTS == STD_ON)
    Eep_IrqEventDeinit();
#endif /* #if (EEP_USE_INTERRUPTS == STD_ON) */
    eepContextPtr->totalSize = 0u;
    eepContextPtr->configPtr = NULL_PTR;
}


#define EEP_STOP_SEC_CODE
#include "Eep_MemMap.h"

#ifdef __cplusplus
}
#endif /* #ifdef __cplusplus */
/* End of file */
